第 7 章:使用 Role 封裝 playbook
什麼是 Ansible Role
前面學習了如何撰寫 Ansible playbook,並將我們的工作清單以 task 的方式在 playbook 中表列下來。然而,這樣充其量我們只能說這是一個比較方便閱讀的 Shell script 罷了。
若是今天我們清單中的任務有上百個,這樣我們的 playbook 也可能會變得非常冗長,就算語法再如何易讀,整體而言 playbook 還是會變得十分難以理解。另外,很多時候其實我們會希望有部分的部署內容是可以被其他不同的 playbook 重新使用。
為了解決上述的問題,Ansible 提供了我們在撰寫自動化腳本時一個角色 (role) 的概念。我們可以透過 撰寫屬於自己的 role 來讓所有 playbook 重複使用,藉此提升透過 Ansible 自動化的靈活度
Roles
就字面上來說有角色、作用的意思,但它的全名其實是 Playbooks Roles,我們可把它當成是 Playbooks 的延伸使用。
在 Python 的世界裡,我們會把寫好的程式封裝成套件 (Packages) 並分享給他人使用,而在 Ansible 的世界裡,則是透過 Roles 做到。
- 將 Playbook 分割成多個文件的抽象化封裝設計
- 一鍵部署,比 Shell Script 更具結構化的腳本語言
- 使用 YAML 格式
- 可使用 Jinja2 (template system) 表達式,並支援變數、判斷式、迴圈…等語法
比對一下 Roles 和 Playbooks 的目錄結構,可以看到前者多了 roles/
目錄和 chusiang.win_vim
的 role。
Playbook role
├─ LICENSE
├─ README.md
├─ ......
├─ ansible.cfg
└─ group_vars
└─ windows.yml
├─ requirements.yml
└─ roles
└─ chusiang.win_vim
├─ LICENSE
├─ README.md
├─ ......
├─ tasks
└─ templates
├─ setup.yml
├─ staging
└─ templates
└─ check_vim_version.bat.j2
playbook
├─ LICENSE
├─ README.md
├─ ......
├─ ansible.cfg
├─ defaults
└─ main.yml
├─ group_vars
└─ windows.yml
├─ setup.yml
├─ tasks
├─ main.yml
└─ use-msi.yml
└─ templates
└─ check_vim_version.bat.j2
Role 的基本結構
tree .
.
└── example_role
├── README.md # 說明文件
├── defaults
│ └── main.yml # 可被覆寫的變數。
├── files # 需複製到 Managed node 的檔案。
├── handlers
│ └── main.yml # 主要的 handler。
├── meta
│ └── main.yml
├── tasks
│ └── main.yml # 主要的 task。
├── templates # 集中存放 Jinja2 模板的目錄。
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml # 不該被覆寫的變數。
9 directories, 8 files
建立第一個 role
考量到在安裝 Docker 的過程中會需要用到 pip 這項工具,同時,這個工具很可能會在未來頻繁地被其他的 playbook 重複使用,因此,我們在這裡就來介紹如何透過 Ansible 來安裝 pip ,並將其寫成一個可以重複被利用的 role ,而非僅僅只是 playbook 中的一個 task。
在 Ubuntu 系統下,一般來說我們可以利用以下這段簡單的指令來安裝 pip:
apt-get update
apt-get install python-pip
作為我們的第一個 Ansible role,讓我們嘗試將這段指令翻譯成 Ansible 的腳本。根據官方文件,Ansible 預設會在以下路徑來尋找可執行的 roles:
- 與被執行 playbook 位於同一層的
roles
資料夾 /etc/ansible/roles
因此,根據這樣的規則,讓我們在工作資料夾下依照以下結構新增檔案 (新增 roles/pip/main.yml
):
workspace
├─ Vagrantfile
├─ inventory
├─ playbook.yml
└─ roles
└─ pip
└─ tasks
└─ main.yml
在這個結構下,pip
就是我們的第一個 role 的名稱,而這個 role 的工作流程就會被我們定義在下面的 tasks/main.yml
之中。現在打開 pip/tasks/main.yml
並在其中寫入以下內容:
main.yml
---
- name: Install pip
apt:
name: python-pip
update_cache: yes
我們在這個 role 的內容中呼叫了 Ansible 內建模組 apt,並利用它來安裝 python-pip 這個套件。其中 update_cache: yes
等效於在安裝前執行 apt-get update
這個指令。接著,打開我們的 playbook.yml
,並修改為以下內容
playbook.yml
---
- hosts: server
roles:
- { role: pip, become: yes }
我們刪除了之前用來測試的 ping 的 play,並在這個 playbook 中告訴 Ansible 我們想要執行 pip
這個我們剛定義好的 role。其中要特別注意的是,become 代表我們要升高當前使用者權限 (等效於 Unix / Linux 中的 sudo
指令)來運行當前工作。
在這裡我們使用了 Ansible 最常見的方式來調用我們剛剛寫好的 role。如果有一連串的 role 要被執行,可以將其定義在 roles 這個 list 之下,比如
定義多個 role
---
- hosts: server
roles:
- { role: pip, become: yes }
- { role: curl, become: yes }
- { role: docker, become: yes }
這樣一來,Ansible 就會依序執行每一個 role。最後,重新運行我們的 playbook,並得到以下結果
運行 playbook
PLAY [server] *****************************************************************
TASK [setup] *******************************************************************
ok: [server]
TASK [pip : Install pip] ******************************************************
changed: [server]
PLAY RECAP *********************************************************************
server : ok=2 changed=1 unreachable=0 failed=0
Roles 可以降低 Playbooks 的複雜性,更可以增加 Playbooks 的可用性
怎麼使用 Roles
我們可以透過 Galaxy (銀河) 和 ansible-galaxy
(Terminal) 來使用 Roles。